#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTime>
#include <QTimer>
#include <QEventLoop>
#include <QMessageBox>
#include <QFileDialog>
#include <QFile>
#include <QProgressDialog>
#include <unistd.h>
#include "crc16.h"


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QStringList listHeader;
    listHeader << "系统时间" << "CAN通道" << "传输方向" << "ID号" << "帧类型" << "帧格式" << "长度" << "数据";

    ui->tableWidget->setColumnCount(listHeader.count());
    ui->tableWidget->setHorizontalHeaderLabels(listHeader);

    ui->tableWidget->setColumnWidth(0,100);
    ui->tableWidget->setColumnWidth(1,70);
    ui->tableWidget->setColumnWidth(2,70);
    ui->tableWidget->setColumnWidth(3,100);
    ui->tableWidget->setColumnWidth(4,70);
    ui->tableWidget->setColumnWidth(5,70);
    ui->tableWidget->setColumnWidth(6,70);
    ui->tableWidget->setColumnWidth(7,180);

//    for(int i = 0;i < 8;i ++)
//        ui->tableWidget->horizontalHeader()->setSectionResizeMode(i,QHeaderView::Stretch);


    ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);

    listHeader.clear();
    listHeader << "节点地址" << "固件类型" << "固件版本";

    ui->tableWidget_NodeList->setColumnCount(listHeader.count());
    ui->tableWidget_NodeList->setHorizontalHeaderLabels(listHeader);

    ui->tableWidget_NodeList->setColumnWidth(0,85);
    ui->tableWidget_NodeList->setColumnWidth(1,85);
    ui->tableWidget_NodeList->setColumnWidth(2,85);

    canthread = new CANThread();
    connect(canthread,&CANThread::getProtocolData,this,&MainWindow::onGetProtocolData);
    connect(canthread,&CANThread::boardInfo,this,&MainWindow::onBoardInfo);
}

MainWindow::~MainWindow()
{
    delete ui;
}

// TODO 建立线程进行更新，能够有效的提高传输速率，可提高速率6倍
// FIXME 上行注释提到的方案，有一部分内容需要学，有些麻烦，此处使用复选框决定是否需要实时显示，亦可达到，后续更具情况增加线程处理
void MainWindow::AddDataToList(QStringList strList)
{
    if(!ui->checkBox_rtdisplay->checkState())
        return;
    int row = ui->tableWidget->rowCount();
    ui->tableWidget->insertRow(row);
    for(int i = 0; i < strList.count();i ++)
    {
        QTableWidgetItem *item = new QTableWidgetItem(strList.at(i),0);
        ui->tableWidget->setItem(row, i, item);
        if(i != strList.count() - 1)
            ui->tableWidget->item(row,i)->setTextAlignment(Qt::AlignCenter | Qt::AlignHCenter);

    }
    ui->tableWidget->scrollToBottom();
}

void MainWindow::ProtocolProcess(VCI_CAN_OBJ *vci)
{

    switch (vci->ID) {
    case MasterEcu2Gcs_Check:
    case SlaveEcu2Gcs_Check:
    case ActuactorEcu2Gcs_Check:
    {
        QStringList nodeList;
        QString str;
        uint32_t fw_type = 0;
        nodeList << QString("%1").arg((vci->ID & 0xf00) >> 8);
//        ui->comboBox_DeviceIndex->addItem(QString("%1").arg((vci->ID & 0xf00) >> 8));

        fw_type = (vci->Data[4] << 24) | (vci->Data[5] << 16) | (vci->Data[6] << 8) | (vci->Data[7]);
        if(fw_type == 0x55555555)
            nodeList << "BootLoader";
        else if(fw_type == 0xAAAAAAAA)
            nodeList << "APP";
        else
            nodeList << "unknown";

        str = QString("v%1.%2").arg((uint16_t)((vci->Data[0] << 8) | (vci->Data[1]))).arg((uint16_t)((vci->Data[2] << 8) | (vci->Data[3])));
        nodeList << str;

        int row = ui->tableWidget_NodeList->rowCount();
        ui->tableWidget_NodeList->insertRow(row);
        for(int i = 0; i < nodeList.count();i ++)
        {
            QTableWidgetItem *item = new QTableWidgetItem(nodeList.at(i),0);
            ui->tableWidget_NodeList->setItem(row, i, item);
            ui->tableWidget_NodeList->item(row,i)->setTextAlignment(Qt::AlignCenter | Qt::AlignHCenter);

        }
        ui->tableWidget_NodeList->scrollToBottom();
    }
        break;

    case MasterEcu2Gcs_Success:
    case SlaveEcu2Gcs_Success:
    case ActuactorEcu2Gcs_Success:
    {
        switch (vci->Data[0]) {
        case 0x11:
            emit EcuReWriteSuccess();
            break;
        case 0x22:
//            qDebug()<<"writeInfo Success!"<<QTime::currentTime().toString("hh:mm:ss zzz");
            emit EcuReWriteInfoSuccess();
            break;

        }
    }
        break;

    case MasterEcu2Gcs_Failed:
    case SlaveEcu2Gcs_Failed:
    case ActuactorEcu2Gcs_Failed:
    {
        QString node;
        node = QString("%1 failed recv").arg((vci->ID & 0xf00) >> 8);
        ui->textBrowser->append(node);
        ui->textBrowser->moveCursor(QTextCursor::End);
        emit EcuReFailed();
    }
        break;

    default:
        break;

    }
}

void MainWindow::onGetProtocolData(VCI_CAN_OBJ *vci,unsigned int dwRel,unsigned int channel)
{
    QStringList messageList;
    QString str;
    for(unsigned int i = 0;i < dwRel;i ++)
    {
        ProtocolProcess(&vci[i]);
        messageList.clear();
        messageList << QTime::currentTime().toString("hh:mm:ss zzz");//时间
        messageList << "CH" + QString::number(channel);
        messageList << "接收";//收发
        messageList << "0x" + QString("%1").arg(vci[i].ID,3,16,QChar('0')).toUpper();//ID
        messageList << ((vci[i].RemoteFlag == 1) ? "远程帧" : "数据帧");//类型
        messageList << ((vci[i].ExternFlag == 1) ? "扩展帧" : "标准帧");//Frame
        QString str = "";
        if(vci[i].RemoteFlag == 0)//数据帧显示数据
        {
            messageList << "0x" + QString::number(vci[i].DataLen,16).toUpper();//长度
            str = "x| ";
            for(int j = 0;j < vci[i].DataLen;j ++)
                str += QString("%1 ").arg(vci[i].Data[j],2,16,QChar('0')).toUpper();//QString::number(recvCANData[i].frame.data[j],16) + " ";
        }
        else
            messageList << "0x0";//长度
        messageList << str;//数据
        AddDataToList(messageList);
    }
}

QString versionStr(USHORT ver)
{
    return "V" + QString::number((ver & 0x0FFF) /0x100,16).toUpper() + "." + QString("%1 ").arg((ver & 0x0FFF) % 0x100,2,16,QChar('0')).toUpper();
}

void MainWindow::updatechart(BYTE dlc,unsigned char* data,UINT CANid)
{
    QStringList messageList;
    messageList.clear();
    messageList << QTime::currentTime().toString("hh:mm:ss zzz");//时间
    messageList << "CH" + QString::number(QVariant(ui->comboBox_CANChannel->currentIndex()).toUInt());
    messageList << "发送";//收发
    messageList << "0x" + QString::number(CANid,16).toUpper() ;//ID
    messageList <<  "数据帧" ;//类型
    messageList <<  "标准帧" ;//Frame
    QString str = "";
    messageList << "0x" + QString::number(dlc,16).toUpper();//长度
    str = "x| ";
    for(int j = 0;j < dlc;j ++)
        str += QString("%1 ").arg(data[j],2,16,QChar('0')).toUpper();//QString::number((unsigned char)data[j],16) + " ";
    messageList << str;//数据
    AddDataToList(messageList);
}

void MainWindow::onBoardInfo(VCI_BOARD_INFO vbi)
{
    ui->label_NumofChannel->setText(QString::number(vbi.can_Num));
    ui->label_vHardware->setText(versionStr(vbi.hw_Version));
    ui->label_vAPI->setText(versionStr(vbi.in_Version));
    ui->label_vInterrupt->setText(QString::number(vbi.irq_Num));
}

void MainWindow::on_pushButton_opendevice_clicked()
{
    if(ui->pushButton_opendevice->text() == tr("打开设备"))
    {
        UINT baundRate = 0;
        if(ui->comboBox_baudrate->currentText().indexOf("Kbps") != -1)
            baundRate = ui->comboBox_baudrate->currentText().remove("Kbps").toUInt();
        else
            baundRate = QVariant(ui->comboBox_baudrate->currentText().remove("Mbps").toFloat()).toUInt();
        bool dev = canthread->openDevice(4,//QVariant(ui->comboBox->currentIndex()).toUInt(),
                                      QVariant(ui->comboBox_deviceIndex->currentIndex()).toUInt(),
                                      baundRate);

        if(dev == true)
        {
            ui->comboBox->setEnabled(false);
            ui->comboBox_deviceIndex->setEnabled(false);
            ui->comboBox_baudrate->setEnabled(false);
            ui->pushButton_InitCAN->setEnabled(true);
            ui->pushButton_opendevice->setText(tr("关闭设备"));
        }
        else
            QMessageBox::warning(this,"警告","打开设备失败！");
    }
    else if(ui->pushButton_opendevice->text() == tr("关闭设备"))
    {
        //ui->comboBox->setEnabled(true);
        ui->comboBox_deviceIndex->setEnabled(true);
        ui->comboBox_baudrate->setEnabled(true);
        ui->comboBox_CANChannel->setEnabled(true);
        ui->pushButton_sendMsg->setEnabled(false);
        ui->pushButton_InitCAN->setEnabled(false);
        ui->pushButton_StartCAN->setEnabled(false);
        ui->pushButton_reset->setEnabled(false);
        ui->pushButton_opendevice->setText(tr("打开设备"));
        canthread->stop();
        canthread->closeDevice();

    }
}

void MainWindow::on_pushButton_InitCAN_clicked()
{
    if(canthread->initCAN())
    {
        ui->pushButton_StartCAN->setEnabled(true);
        ui->pushButton_InitCAN->setEnabled(false);
    }
    else
        QMessageBox::warning(this,"警告","CAN初始化失败！");
}

void MainWindow::on_pushButton_StartCAN_clicked()
{
    if(canthread->startCAN())
    {
        ui->pushButton_StartCAN->setEnabled(false);
        ui->pushButton_reset->setEnabled(true);
        ui->pushButton_sendMsg->setEnabled(true);
        ui->pushButton_ScanNode->setEnabled(true);
        canthread->start();
    }
    else
        QMessageBox::warning(this,"警告","CAN启动失败！");
}

void MainWindow::on_pushButton_reset_clicked()
{
    if(canthread->reSetCAN())
    {
        ui->pushButton_reset->setEnabled(false);
        ui->pushButton_sendMsg->setEnabled(false);
        ui->pushButton_StartCAN->setEnabled(true);
    }
    else
        QMessageBox::warning(this,"警告","CAN复位失败！");
}

void MainWindow::on_pushButton_sendMsg_clicked()
{
    if(ui->comboBox_STDorEXT->currentIndex() == 0)//标准帧，ID 范围0-0x7FF
    {
        if(ui->lineEdit_SendID->text().toInt(Q_NULLPTR,16) > 0x7FF)
        {
            QMessageBox::warning(this,"警告","发送失败，标准帧ID范围为0~0x7FF！");
            return;
        }
    }
    else
    {
        if(ui->lineEdit_SendID->text().toInt(Q_NULLPTR,16) > 0x1FFFFFFF)
        {
            QMessageBox::warning(this,"警告","发送失败，扩展帧ID范围为0~0x1FFFFFFF！");
            return;
        }
    }
    QStringList strList = ui->lineEdit_data->text().split(" ");
    unsigned char data[8];
    memset(data,0,8);
    UINT dlc = 0;
    dlc = strList.count() > 8 ? 8 : strList.count();
    UINT can_id;
    can_id = QVariant(ui->lineEdit_SendID->text().toInt(Q_NULLPTR,16)).toUInt();
    for(int i = 0;i < dlc;i ++)
        data[i] = strList.at(i).toInt(Q_NULLPTR,16);
    if(canthread->sendData(QVariant(ui->comboBox_CANChannel->currentIndex()).toUInt(),
                           can_id,
                           ui->comboBox_RTR->currentIndex(),
                           ui->comboBox_STDorEXT->currentIndex(),
                           data,dlc))
    updatechart(dlc,data,can_id);
    else
        QMessageBox::warning(this,"警告","数据发送失败！");
}

void MainWindow::on_pushButton_ClearRecv_clicked()
{
    ui->tableWidget->setRowCount(0);
}

void MainWindow::on_pushButton_ScanNode_clicked()
{
    ui->tableWidget_NodeList->setRowCount(0); //清空当前列表
    unsigned char data[8];
    memset(data,0,8);
    BYTE dlc = 0;
    dlc = 8;
    if(canthread->sendData(QVariant(ui->comboBox_CANChannel->currentIndex()).toUInt(),
                           Gcs2allEcu_Check,
                           0,
                           0,
                           data,dlc))
        updatechart(dlc,data,Gcs2allEcu_Check);
    else
        QMessageBox::warning(this,"警告","扫描节点失败！");
}

void MainWindow::on_pushButton_OpenFile_clicked()
{
    QString path = QFileDialog::getOpenFileName(this,"open files",QDir::currentPath(),"Binary Files (*.bin);;Hex Files (*.hex);;All Files (*.*)");
    ui->lineEdit_FileName->setText(path);
}

void MainWindow::sendPacketInfo(unsigned int packet_size,unsigned int flash_offset)
{
    unsigned char data[8];
    memset(data,0,8);
    uint8_t dlc = 8;

    data[0] = (flash_offset & 0xff000000) >> 24;
    data[1] = (flash_offset & 0x00ff0000) >> 16;
    data[2] = (flash_offset & 0x0000ff00) >> 8;
    data[3] = (flash_offset & 0x000000ff) >> 0;
    data[4] = (packet_size & 0xff000000) >> 24;
    data[5] = (packet_size & 0x00ff0000) >> 16;
    data[6] = (packet_size & 0x0000ff00) >> 8;
    data[7] = (packet_size & 0x000000ff) >> 0;


    uint8_t node_addr;
    if(ui->checkBox_allEcu->isChecked())
        node_addr = 0;
    else
        node_addr = ui->comboBox_EcuIndex->currentIndex();// FIXME 后续增加选择ID号程序
    if(canthread->sendData(QVariant(ui->comboBox_CANChannel->currentIndex()).toUInt(),
                           getCANID(node_addr,1),// FIXME 后续修改为node_addr
                           0,
                           0,
                           data,dlc))
        updatechart(dlc,data,getCANID(node_addr,1));
    else
        QMessageBox::warning(this,"警告","帧数据信息发送失败！");
}

void MainWindow::sendPacket(uint8_t* packet_data,unsigned int packet_size)
{
    unsigned int i = 0;
    uint8_t tx_data[8];
    uint8_t send_packet[1028];
    uint8_t dlc;
    uint16_t crc_data = crc16_ccitt(packet_data,packet_size);
    memcpy(send_packet,packet_data,packet_size);
    send_packet[packet_size] = (crc_data & 0xff00) >> 8;
    send_packet[packet_size+1] = (crc_data & 0x00ff) >> 0;

    for(i = 0;i<packet_size + 2;i+=8)
    {
        dlc = (packet_size - i + 2) >= 8 ? 8 :(packet_size - i + 2);
        memset(tx_data,0,8);
        memcpy(tx_data,send_packet + i,dlc);
        uint8_t node_addr;
        if(ui->checkBox_allEcu->isChecked())
            node_addr = 0;
        else
            node_addr = (uint8_t)(ui->comboBox_EcuIndex->currentIndex());// FIXME 后续增加选择ID号程序
        if(canthread->sendData(QVariant(ui->comboBox_CANChannel->currentIndex()).toUInt(),
                               getCANID(node_addr,2),// FIXME 后续修改为node_addr
                               0,
                               0,
                               tx_data,dlc))
            updatechart(dlc,tx_data,getCANID(node_addr,2));
        else
            QMessageBox::warning(this,"警告","帧数据发送失败！");
    }
}

void MainWindow::on_pushButton_UpdateFirmware_clicked()
{
    QTime time;
    time.start();

    // Step 1: check the node, e.g. status&AppType


    // Step 2: open the file
    QFile firmwareFile(ui->lineEdit_FileName->text());
    if(firmwareFile.open(QFile::ReadOnly))
    {

        // Step 3: create the processDialog
        QProgressDialog writeDataProcess("正在更新固件...","取消",0,firmwareFile.size(),this);
        writeDataProcess.setWindowTitle("更新固件");
        writeDataProcess.setModal(true);//更新固件过程不可操作其他窗口
        writeDataProcess.show();
        QCoreApplication::processEvents(QEventLoop::AllEvents);

        // Step 4: read the data to buffer
        int read_data_num;
        uint8_t FirmwareData[1026];
        int i = 0;
        int PackSize = 1024;
        uint16_t node_addr;
        uint32_t addr_offset = 0;
        for(i = 0;i<firmwareFile.size();i+=PackSize)
        {
            QEventLoop Writeloop;
            QEventLoop WriteInfoloop;
            QTimer Writetimer;
            QTimer WriteInfotimer;
            int timeout = 5000;
            bool isWriteResponseReceived = false;
            bool isWriteInfoResponseReceived = false;

            // 连接PacketInfo信号与槽
            connect(this,&MainWindow::EcuReWriteInfoSuccess,[&](void){
                isWriteInfoResponseReceived = true;
                WriteInfoloop.quit();
            });
            connect(&WriteInfotimer,&QTimer::timeout,&WriteInfoloop,&QEventLoop::quit);


            // 连接Packet发送信号与槽
            connect(this,&MainWindow::EcuReWriteSuccess,[&](void){
                isWriteResponseReceived = true;
                Writeloop.quit();
            });
            connect(&Writetimer,&QTimer::timeout,&Writeloop,&QEventLoop::quit);


            read_data_num = firmwareFile.read((char*)FirmwareData,PackSize);

            // step 5: send packet info
            // TODO 增加事件循环，等待触发
            sendPacketInfo(read_data_num+2,addr_offset);
            WriteInfotimer.start(timeout);
            WriteInfoloop.exec();

            if(!isWriteInfoResponseReceived)
            {
                QMessageBox::warning(this,"警告","PacketInfo Response通信连接超时");
                return;
            }

            // Step 6: send data for a packet via CAN & wait for response
            sendPacket(FirmwareData,read_data_num);
            Writetimer.start(timeout);
            Writeloop.exec();

            addr_offset+=read_data_num;
            if(!isWriteResponseReceived)
            {
                QMessageBox::warning(this,"警告","Packet Response通信连接超时");
                return;
            }


            // update the process bar
            writeDataProcess.setValue(i);
            QCoreApplication::processEvents(QEventLoop::AllEvents);
            if(writeDataProcess.wasCanceled())
            {
                return;
            }
        }
    }
    else
    {
        QMessageBox::warning(this,"警告","打开固件文件失败！");
        return ;
    }
    // Step 7: run the APP


    // Step 8: output the time elapsed
    qDebug()<<time.elapsed()/1000.0<<"s"; /* 后续可改为界面显示，当前为输出窗口显示 */
    QString node;
    node = QString("[%1]:%2 success recv the Firmware, take %3 s").arg(QTime::currentTime().toString("hh:mm:ss zzz")).arg(ui->comboBox_EcuIndex->currentText()).arg(time.elapsed()/1000.0);
    ui->textBrowser->append(node);
    ui->textBrowser->moveCursor(QTextCursor::End);

}

void MainWindow::on_comboBox_CANChannel_currentIndexChanged(int index)
{
    ui->comboBox_CANChannelofFunction->setCurrentIndex(index);
}

void MainWindow::on_comboBox_CANChannelofFunction_currentIndexChanged(int index)
{
    ui->comboBox_CANChannel->setCurrentIndex(index);
}

void MainWindow::on_pushButton_Execute_clicked()
{
    unsigned char data[8];
    memset(data,0,8);
    BYTE dlc = 0;
    dlc = 8;
    if(canthread->sendData(QVariant(ui->comboBox_CANChannel->currentIndex()).toUInt(),
                           Gcs2allEcu_Execute,
                           0,
                           0,
                           data,dlc))
        updatechart(dlc,data,Gcs2allEcu_Execute);
    else
        QMessageBox::warning(this,"警告","扫描节点失败！");
}
